
#include "StdAfx.h"
#include "DBService.h"
#include "ODBC_DBError.h"

#include <tinyxml.h>
#include <StringObj.h>
#include <LogService.h>
#include <DeleteObjInSTL.h>
#include <TickSaver.h>
#include <algorithm>
#include <StaticProcessInfo.h>

#ifdef WIN32
#pragma comment( lib, "tinyxml.lib" )
#endif

DBService::DBService(){
	session_arrange_sec_ = 0;
	check_db_sec_ = 0;
	reload_env_sec_ = 0;
}

DBService::~DBService(){
	stop();	
}

DBService* DBService::Instance(){
	if(! DBService_ )
		DBService_ = new DBService();
	
	return DBService_;
}

void DBService::Shutdown(){
	SAFE_DELETE( DBService_ );
}

DEF_THREAD_RETURN DBService::run(){
	LOG_INSTANCE( LOG_SERVICE( "instance" )->Log("thread start %s = %u" \
			, __FILE__, getThreadID()) );

	DBSessionManager* sessions = 0;
	TickSaver tick_saver;

	size_t nTime = (size_t)time( 0 );
	tick_saver.reset( 4 );
	tick_saver.store_sec( 0, nTime );
	tick_saver.store_sec( 1, nTime );
	tick_saver.store_sec( 2, nTime );
	tick_saver.store_sec( 3, nTime );	

	int log_2nd_sec = DEF_LOG_2ND_SESSION_TIME;	

	while( isActive() ){
		cycle_.adjustCycle();

		stat_.add_main_loop_count();

		if( tick_saver.is_elapsed_sec( 0, session_arrange_sec_, cycle_.lastSec() ) ){
			for( size_t i = 0 ; i < session_.size() ; i++ ){
				sessions = session_[ i ];
				IS_NULL_ACTION( sessions, continue );

				sessions->arrangement();
				
				// jds83 - dbstat
				stat_.set( (char*)sessions->get_name(), sessions->getFreeSize(), sessions->getUsedSize() );
			}
			
			tick_saver.store_sec( 0, cycle_.lastSec() );
		}
		
		if( tick_saver.is_elapsed_sec( 1, reload_env_sec_, cycle_.lastSec() ) ){
			setEnvironment();
			tick_saver.store_sec( 1, cycle_.lastSec() );
		}

		if( tick_saver.is_elapsed_sec( 2, check_db_sec_, cycle_.lastSec() ) ){
			size_t i = 0;			

			for( i = 0 ; i < session_.size() ; i++ ){
				sessions = session_[ i ];				
				
				if( sessions )
					sessions->check_dbms();
			}

			tick_saver.store_sec( 2, cycle_.lastSec() );
		}

		if( tick_saver.is_elapsed_sec( 3, log_2nd_sec, cycle_.lastSec() ) ){
			DBSessionManager* sessionMgr2nd = 0;
			char* name_1st = 0;
			char* name_2nd = 0;

			for( size_t i = 0 ; i < session_.size() ; i++ ){
				sessions = session_[ i ];
				IS_NULL_ACTION( sessions, continue );		
				
				name_1st = (char*)sessions->get_name();
				name_2nd = (char*)sessions->get_second_name();
				
				if(! name_2nd )
					continue;

				if( sessions->is_available_db() )
					continue;

				sessionMgr2nd = getSessionManager( name_2nd );

				if(! sessionMgr2nd )
					continue;

				if( sessionMgr2nd->is_available_db() ){
					LOG_INSTANCE( LOG_SERVICE( "instance" )->Log("%s is 2nd session( %s ) running...", name_1st, name_2nd ) );	
				}
			}

			tick_saver.store_sec( 3, cycle_.lastSec() );
		}		
	}

	LOG_INSTANCE( LOG_SERVICE( "instance" )->Log("thread end %s = %u" \
			, __FILE__, getThreadID()) );
}

int DBService::initialize( const char* env_file_name, size_t service_id ){
	IS_NULL_RETURN( env_file_name, -1 );

	env_file_name_ = env_file_name;

	LOG_INSTANCE( LOG_SERVICE( "instance" )->Log( "* db initialize value : file name = %s", env_file_name ) );

	// jds83 - dbstat	
	if( service_id )
		stat_.initialize( (char*)env_file_name );

	if( setEnvironment() )
		return -1;

	start( 0, this );

	return 0;
}

DBSessionManager* DBService::getSessionManager( const char* name ){
	BlanketLock lock( &lock_ );

	IS_NULL_RETURN( name, 0 );

	if(! strlen( name ) )
		return 0;

	DBSessionManager* mgr = 0;
	for( size_t i = 0 ; i < session_.size() ; i++ ){
		mgr = session_[ i ];

		IS_NULL_ACTION( mgr, continue );
		
		if(! strncmp( mgr->get_name(), name, strlen( name ) ) )
			return mgr;
	}

	return 0;
}

int DBService::loadDBSessionInfo(){
	TiXmlDocument doc( env_file_name_.c_str() );

	if( doc.LoadFile() == false ){
		int line = doc.ErrorRow();
		int col = doc.ErrorCol();

		LOG_ERR( LOG_SERVICE( "instance" )->Log \
			("error load db session info xml = line : %u, col : %u", line, col) );

		return -1;
	}

	if( doc.Error() )
		return -1;
	
	TiXmlNode* node = 0;
	TiXmlElement* element = doc.RootElement();

	StringObj value;
	value = (char*)element->Value();
	if(! value.isEqualIgnLetterType("config") )
		return -1;

	node = element->FirstChild("db_session_info");

	StringObj api;
	StringObj name;
	StringObj ip;
	StringObj id;
	StringObj passwd;
	StringObj port;
	StringObj min;
	StringObj max;
	StringObj wait_time_tick;
	StringObj wait_count;
	StringObj idle_remove_time;
	StringObj available_check_sql;
	StringObj prepare_conn_sql;
	StringObj odbc_option;
	StringObj db;	
	StringObj second_name;

	TiXmlNode* prepare_node = 0;	

	for( node = node->FirstChild() ; node != 0 ; node = node->NextSibling()){
		api.clear();
		name.clear();
		ip.clear();
		id.clear();
		passwd.clear();
		port.clear();
		min.clear();
		max.clear();
		wait_time_tick.clear();
		wait_count.clear();
		idle_remove_time.clear();
		available_check_sql.clear();
		prepare_conn_sql.clear();
		odbc_option.clear();
		db.clear();
		second_name.clear();

		node->ToElement();		
		name = node->FirstChild( "name" )->FirstChild()->Value();
		id = node->FirstChild( "id" )->FirstChild()->Value();
		ip = node->FirstChild( "ip" )->FirstChild()->Value();		
		passwd = node->FirstChild( "passwd" )->FirstChild()->Value();		
		port = node->FirstChild( "port" )->FirstChild()->Value();
		min = node->FirstChild( "min" )->FirstChild()->Value();
		max = node->FirstChild( "max" )->FirstChild()->Value();
		wait_time_tick = node->FirstChild( "wait_time_tick" )->FirstChild()->Value();
		wait_count = node->FirstChild( "wait_count" )->FirstChild()->Value();
		idle_remove_time = node->FirstChild( "idle_remove_time" )->FirstChild()->Value();
		
		prepare_node = node->FirstChild( "available_check" );
		if( prepare_node ){
			if( prepare_node->FirstChild() )
				available_check_sql = prepare_node->FirstChild()->Value();
		}

		prepare_node = node->FirstChild( "api" );
		if( prepare_node ){
			if( prepare_node->FirstChild())
				api = prepare_node->FirstChild()->Value();	
		}
		
		prepare_node = node->FirstChild( "prepare_conn_sql" );
		if( prepare_node ){
			if( prepare_node->FirstChild())
				prepare_conn_sql = prepare_node->FirstChild()->Value();	
		}
		
		prepare_node = node->FirstChild( "odbc_option" );
		if( prepare_node ){
			if( prepare_node->FirstChild())
				odbc_option = prepare_node->FirstChild()->Value();
		}

		prepare_node = node->FirstChild( "db" );
		if( prepare_node ){
			if( prepare_node->FirstChild())
				db = prepare_node->FirstChild()->Value();		
		}
		
		prepare_node = node->FirstChild( "second_connection" );
		if( prepare_node ){
			if( prepare_node->FirstChild())
				second_name = prepare_node->FirstChild()->Value();		
		}
		
		initialize_session( api.toCharacters(), min.to4ByteNumber(), max.to4ByteNumber(), wait_time_tick.to4ByteNumber(), \
			wait_count.to4ByteNumber(), idle_remove_time.to4ByteNumber(), port.to4ByteNumber(), \
			name.toCharacters(), ip.toCharacters(), id.toCharacters(), passwd.toCharacters(), \
			available_check_sql.toCharacters(), prepare_conn_sql.toCharacters(), second_name.toCharacters(), db.toCharacters() );
	}

	return 0;
}

int DBService::initialize_session( char* api, int min, int max, int wait_time_tick, int wait_count, int idle_remove_time, int port, \
			char* name, char* ip, char* id, char* passwd, char* available_check_sql, char* prepare_conn_sql, char* second_name ,char* db  )
{
	DBSessionManager* sessionMgr = 0;		
	sessionMgr = getSessionManager( name );

	if( sessionMgr ){
		sessionMgr->reset( min, max, wait_time_tick, wait_count, idle_remove_time, name ,second_name );
	}
	else{
		sessionMgr = new DBSessionManager();
		sessionMgr->initialize( name, ip, db, id, passwd, port, available_check_sql, min, \
			max, wait_time_tick, wait_count, idle_remove_time, prepare_conn_sql , second_name, api );

		session_.push_back( sessionMgr );

		// jds83 - dbstat				
		stat_.set( (char*)sessionMgr->get_name(), sessionMgr->getFreeSize(), sessionMgr->getUsedSize() );
	}	

	return DEFAULT_SUCCESS;
}

int DBService::loadServiceInfo(){
	TiXmlDocument doc( env_file_name_.c_str() );

	if( doc.LoadFile() == false ){
		int line = doc.ErrorRow();
		int col = doc.ErrorCol();

		LOG_ERR( LOG_SERVICE( "instance" )->Log \
			("error load db service info xml = line : %u, col : %u", line, col) );

		return -1;
	}
	
	if( doc.Error() )
		return -1;
	
	TiXmlNode* node = 0;
	TiXmlElement* element = doc.RootElement();

	StringObj value;
	value = (char*)element->Value();
	if(! value.isEqualIgnLetterType("config") )
		return -1;

	node = element->FirstChild("db");

	if(! node ){
		LOG_ERR( LOG_SERVICE( "instance" )->Log( "error load db attribute" ) );
		return -1;
	}

	value = node->FirstChild("cycle_tick")->FirstChild()->Value();
	cycle_.setTickPerCycle( value.to4ByteNumber() );

	value = node->FirstChild("session_arrange_sec")->FirstChild()->Value();
	session_arrange_sec_ = value.to4ByteNumber();

	value = node->FirstChild("check_db_sec")->FirstChild()->Value();
	check_db_sec_ = value.to4ByteNumber();
	
	value = node->FirstChild("reload_env_sec")->FirstChild()->Value();
	reload_env_sec_ = value.to4ByteNumber();

	return 0;
}

int DBService::setEnvironment(){
	if( loadServiceInfo() ){
		LOG_ERR( LOG_SERVICE( "instance" )->Log( "%s - %u", __FUNCTION__, __LINE__ ) );
		return -1;
	}

	if( loadDBSessionInfo() ){
		LOG_ERR( LOG_SERVICE( "instance" )->Log( "%s - %u", __FUNCTION__, __LINE__ ) );
		return -1;
	}

	return 0;
}
